package io.github.wppli.domain.recommond.rservice.text;

import cc.jq1024.middleware.redisson.IRedissonService;
import io.github.wppli.domain.posts.repository.IPostsRepository;
import io.github.wppli.domain.posts.service.IPostsService;
import io.github.wppli.domain.recommond.model.entity.ContentFeatureEntity;
import io.github.wppli.domain.recommond.model.entity.PostsContentEntity;
import io.github.wppli.domain.recommond.model.entity.UserPostsBehaviorsEntity;
import io.github.wppli.domain.recommond.rservice.IContentFeatureService;
import io.github.wppli.domain.recommond.rservice.IUserPostsBehaviorService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 内容推荐服务
 * @author li--jiaqiang 2025−03−15
 */
@Slf4j
@Service
@AllArgsConstructor
public class ContentRecommenderService {

    private final IContentFeatureService contentFeatureService;
    private final IUserPostsBehaviorService userPostsBehaviorService;
    private final IPostsRepository postsRepository;
    private final TextFeatureService textFeatureService;
    private final IPostsService postsService;
    private final IRedissonService redissonService;

    /**
     * 为用户生成内容推荐
     */
    public List<PostsContentEntity> recommendContent(Long userId, int topN) {
        // 1. 获取用户历史行为内容
        List<UserPostsBehaviorsEntity> behaviors = userPostsBehaviorService.queryByUserId(userId);
        List<Long> contentIds = behaviors.stream()
                .map(UserPostsBehaviorsEntity::getPostsContentId)
                .collect(Collectors.toList());

        // 2. 构建用户画像（合并特征向量）
        Map<String, Double> userProfile = new HashMap<>();
        List<ContentFeatureEntity> features = contentFeatureService.findByContentIds(contentIds);
        for (ContentFeatureEntity feature : features) {
            if (feature == null) continue;
            Map<String, Double> vec = textFeatureService.parseFeatureVector(feature.getFeatureVector());
        /*
            遍历 vec 中的每一个键值对
            for (Map.Entry<String, Double> entry : vec.entrySet()) {
                String key = entry.getKey();
                Double value = entry.getValue();

                // 检查 userProfile 中是否已经存在该键
                if (userProfile.containsKey(key)) {
                    // 如果存在，将旧值和新值相加
                    Double oldValue = userProfile.get(key);
                    userProfile.put(key, oldValue + value);
                } else {
                    // 如果不存在，直接将键值对添加到 userProfile 中
                    userProfile.put(key, value);
                }
            }
        */
            // 当键已经存在于 userProfile 中时，用于合并旧值和新值的函数。
            vec.forEach((k, v) -> userProfile.merge(k, v, Double::sum));
        }

        // 3. 计算所有内容与用户画像的相似度
        List<ContentFeatureEntity> allFeatures = contentFeatureService.findAll();
        Map<Long, Double> scores = new HashMap<>();
        for (ContentFeatureEntity feature : allFeatures) {
            if (contentIds.contains(feature.getContentId())) continue; // 排除用户已经看过的内容
            Map<String, Double> contentVec = textFeatureService.parseFeatureVector(feature.getFeatureVector());
            double similarity = cosineSimilarity(userProfile, contentVec);
            scores.put(feature.getContentId(), similarity);
        }

        // 4. 返回TopN内容
        List<Long> PostsContentIds = scores.entrySet().stream()
                .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
                .limit(topN)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
        return postsRepository.queryBatchIds(PostsContentIds);
    }

    // 计算余弦相似度
    private double cosineSimilarity(Map<String, Double> vec1, Map<String, Double> vec2) {
        Set<String> commonKeys = new HashSet<>(vec1.keySet());
        commonKeys.retainAll(vec2.keySet());
        if (commonKeys.isEmpty()) {
            return 0.0;
        }
        double dotProduct = commonKeys.stream().mapToDouble(k -> vec1.get(k) * vec2.get(k)).sum();
        double norm1 = Math.sqrt(vec1.values().stream().mapToDouble(v -> v * v).sum());
        double norm2 = Math.sqrt(vec2.values().stream().mapToDouble(v -> v * v).sum());
        return dotProduct / (norm1 * norm2);
    }


    /**
     * 根据{keywords}为用户生成内容推荐
     * @param keywords 关键词
     * @return 推荐内容列表
     */
    public List<PostsContentEntity> recommendContent(String keywords, int topN) {
        try {
            if (topN <= 0) {
                topN = 10;
            }

            // 从缓存中获取关键词
            String key = "recommend_content_keywords_" + keywords + "_topN" + topN;
            List<PostsContentEntity> cachePostsContents = redissonService.getList(key, PostsContentEntity.class);
            if (cachePostsContents != null && !cachePostsContents.isEmpty()) {
                log.info("根据keywords：{} topN:{} 为用户生成内容推荐 - 缓存命中。", keywords, topN);
                return cachePostsContents;
            }

            // 1. 计算用户输入内容的 TF-IDF
            List<PostsContentEntity> allContents = postsService.queryAll();
            Map<String, Double> inputTFIDF = textFeatureService.calculateTFIDF(keywords, allContents);
            // 2. 计算与所有候选内容的相似度
            List<ContentFeatureEntity> allFeatures = contentFeatureService.findAll();
            Map<Long, Double> scores = new HashMap<>();
            for (ContentFeatureEntity feature : allFeatures) {
                Map<String, Double> contentVec = textFeatureService.parseFeatureVector(feature.getFeatureVector());
                double similarity = cosineSimilarity(inputTFIDF, contentVec);
                scores.put(feature.getContentId(), similarity);
            }
            // 3. 返回相似度最高的内容
            List<Long> postsContentIds = scores.entrySet().stream()
                    .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
                    .limit(topN)
                    .map(Map.Entry::getKey)
                    .collect(Collectors.toList());

            // 创建一个 Map 用于快速查找 ID 对应的 PostsContentEntity
            Map<Long, PostsContentEntity> idToEntityMap = allContents.stream()
                    .collect(Collectors.toMap(PostsContentEntity::getId, entity -> entity));

            // 按照 postsContentIds 的顺序从 Map 中获取对应的 PostsContentEntity
            List<PostsContentEntity> postsContentEntities = postsContentIds.stream()
                    .map(idToEntityMap::get)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toList());
            redissonService.setValue(key, postsContentEntities, 60 + new Random().nextInt(20), TimeUnit.MINUTES);
            return postsContentEntities;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("获取内容推荐失败");
        }
    }
}